#!/usr/bin/env ruby
require 'rubygems'
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib')))
require "bundler/setup"
require 'logger'
require 'rdf/trig'
require 'rdf/isomorphic'
require File.expand_path(File.join(File.dirname(__FILE__), "..", 'spec', 'spec_helper'))
require File.expand_path(File.join(File.dirname(__FILE__), "..", 'spec', 'suite_helper'))
require 'getoptlong'

ASSERTOR = "http://greggkellogg.net/foaf#me"
RUN_TIME = Time.now

def earl_preamble(options)
  options[:output].write File.read(File.expand_path("../../etc/doap#{'-nquads' if options[:nquads]}.ttl", __FILE__))
  options[:output].puts %(
<> foaf:primaryTopic <http://rubygems.org/gems/rdf#{'-trig' unless options[:nquads]}> ;
  dc:issued "#{RUN_TIME.xmlschema}"^^xsd:dateTime ;
  foaf:maker <#{ASSERTOR}> .

<#{ASSERTOR}> a foaf:Person, earl:Assertor;
  foaf:name "Gregg Kellogg";
  foaf:title "Implementor";
  foaf:homepage <http://greggkellogg.net/> .
)

  options[:output].puts options[:nquads] ? %(
<http://rubygems.org/gems/rdf>
  doap:release <https://github.com/ruby-rdf/rdf/tree/#{RDF::VERSION}> .

<https://github.com/ruby-rdf/rdf/tree/#{RDF::VERSION}> a doap:Version;
  doap:name "RDF.rb-#{RDF::VERSION}";
  doap:revision "#{RDF::VERSION}" .
) : %(
<http://rubygems.org/gems/rdf-trig>
  doap:release <https://github.com/ruby-rdf/rdf-trig/tree/#{RDF::TriG::VERSION}> .

<https://github.com/ruby-rdf/rdf-trig/tree/#{RDF::TriG::VERSION}> a doap:Version;
  doap:name "rdf-turtle-#{RDF::TriG::VERSION}";
  doap:created "#{File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d')}"^^xsd:date;
  doap:revision "#{RDF::TriG::VERSION}" .
)
end

def run_tc(tc, options)
  STDERR.write "run #{tc.name}"

  if options[:verbose]
    puts "\nTestCase: #{tc.inspect}"
    puts "\nInput:\n" + tc.input 
    puts "\nExpected:\n" + tc.expected
  end

  begin
    puts "open #{tc.action}" if options[:verbose]
    tc.warnings = []
    tc.errors = []
    options = {
      base_uri:  tc.base,
      warnings:  tc.warnings,
      errors:    tc.errors,
      validate:  true
    }.merge(options)

    reader = RDF::Reader.for(tc.action).new(tc.input, options)

    graph = RDF::Repository.new
    result = nil

    if tc.positive_test?
      begin
        graph << reader
      rescue Exception => e
        STDERR.puts "Unexpected exception: #{e.inspect}" if options[:verbose]
        result = "failed"
      end
    else
      begin
        graph << reader
        STDERR.puts "Expected exception" if options[:verbose]
        result = "failed"
      rescue RDF::ReaderError
        result = "passed"
      end
    end

    if tc.evaluate? && result.nil?
      output_graph = RDF::Repository.load(tc.result, :format => :nquads, :base_uri => tc.base)
      result = graph.isomorphic_with?(output_graph) ? "passed" : "failed"
    else
      result ||= "passed"
    end

  rescue Exception => e
    STDERR.puts "#{"exception:" unless options[:quiet]}: #{e}"
    if options[:quiet]
      return
    else
      raise
    end
  end
  
  if options[:earl]
    options[:output].puts %{
[ a earl:Assertion;
  earl:assertedBy <#{ASSERTOR}>;
  earl:subject <http://rubygems.org/gems/rdf#{'-trig' unless options[:nquads]}>;
  earl:test <#{tc.id}>;
  earl:result [
    a earl:TestResult;
    earl:outcome earl:#{result};
    dc:date "#{RUN_TIME.xmlschema}"^^xsd:dateTime];
  earl:mode earl:automatic ] .
}
  end

  options[:result_count][result] ||= 0
  options[:result_count][result] += 1

  puts "#{"test result:" unless options[:quiet]} #{result}"
end

logger = Logger.new(STDERR)
logger.level = Logger::WARN
logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity}: #{msg}\n"}

options = {
  output:  STDOUT,
  logger: logger
}

opts = GetoptLong.new(
  ["--help", "-?", GetoptLong::NO_ARGUMENT],
  ["--dbg", GetoptLong::NO_ARGUMENT],
  ["--earl", GetoptLong::NO_ARGUMENT],
  ["--quiet", "-q", GetoptLong::NO_ARGUMENT],
  ["--nquads", GetoptLong::NO_ARGUMENT],
  ["--output", "-o", GetoptLong::REQUIRED_ARGUMENT],
  ["--skip-long", "-s", GetoptLong::NO_ARGUMENT],
  ["--validate", GetoptLong::NO_ARGUMENT],
  ["--verbose", "-v", GetoptLong::NO_ARGUMENT]
)

def help(options)
  puts "Usage: #{$0} [options] [test-number ...]"
  puts "Options:"
  puts "      --debug:              Display detailed debug output"
  puts "      --earl:               Generate EARL report"
  puts "      --quiet:              Minimal output"
  puts "      --nquads:             Run N-Quads tests"
  puts "      --output:             Output to specified file"
  puts "      --skip-long:          Avoid files taking too much time"
  puts "      --validate:           Validate input"
  puts "      --verbose:            Verbose processing"
  puts "      --help,-?:            This message"
  exit(0)
end

opts.each do |opt, arg|
  case opt
  when '--help'             then help(options)
  when '--dbg'              then logger.level = Logger::DEBUG
  when '--earl'
    options[:quiet] = options[:earl] = true
    logger.level = Logger::FATAL
  when '--nquads'           then options[:nquads] = true
  when '--output'           then options[:output] = File.open(arg, "w")
  when '--quiet'
    options[:quiet] = true
    logger.level = Logger::FATAL
  when '--skip-long'        then options[:skip] = true
  when '--validate'         then options[:validate] = true
  when '--verbose'          then options[:verbose] = true
  end
end

manifests = (options[:nquads] ? [Fixtures::SuiteTest::NQBASE] : [Fixtures::SuiteTest::BASE]).map {|b| b + "manifest.ttl"}

earl_preamble(options) if options[:earl]

result_count = {}

manifests.each do |manifest|
  Fixtures::SuiteTest::Manifest.open(manifest) do |m|
    m.entries.each do |tc|
      next unless ARGV.empty? || ARGV.any? {|n| tc.name.match(/#{n}/)}
      run_tc(tc, options.merge(result_count: result_count))
    end
  end
end

result_count.each do |result, count|
  puts "#{result}: #{count}"
end
